﻿using System;
using System.Collections.ObjectModel;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Messaging;
using Miris.HumanMilkAnalyser.DataModel;
using Miris.HumanMilkAnalyser.Service;
using Miris.HumanMilkAnalyser.Service.Shared;
using Miris.HumanMilkAnalyser.WPF.Model;
using Miris.HumanMilkAnalyser.WPF.Shared;
using Miris.HumanMilkAnalyser.WPF.ViewModel.TestItemViewModel;

namespace Miris.HumanMilkAnalyser.WPF.ViewModel
{
    /// <summary>
    /// This class contains properties that a View can data bind to.
    /// <para>
    /// See http://www.galasoft.ch/mvvm
    /// </para>
    /// </summary>
    public class MainPageViewModel : MyViewModelBase
    {
        public const string GoToNextPageMessageToken = "MainPageGoToNextPageMessage";
        public MilkSample CurrentMilkSample { get; private set; }
        public MilkStage CurrentMilkStage { get; private set; }
        public AnalysisResult CurrentAnalysisResult { get; private set; }

        /// <summary>
        /// Initializes a new instance of the MainPageViewModel class.
        /// </summary>
        public MainPageViewModel(IDataService dataService)
            : base(dataService) { }

        public override void ResetProperties()
        {
            base.ResetProperties();

            GeneratedSampleNumber = string.Empty;
            InputSampleNumber = string.Empty;

            IsInputSampleNumberReadOnly = false;

            IsFrontMilkSample = false;
            IsMiddleMilkSample = false;
            IsEndMilkSample = false;
            IsFullMilkSample = false;

            IsUnmatureMilkStage = false;
            IsTransitionalMilkStage = false;
            IsMatureMilkStage = false;

            AnalysisResultList.Clear();
        }

        /// <summary>
        /// The <see cref="GeneratedSampleNumber" /> property's name.
        /// </summary>
        public const string GeneratedSampleNumberPropertyName = "GeneratedSampleNumber";

        private string _generatedSampleNumber = string.Empty;

        /// <summary>
        /// Sets and gets the GeneratedSampleNumber property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public string GeneratedSampleNumber
        {
            get
            {
                return _generatedSampleNumber;
            }

            set
            {
                if (_generatedSampleNumber == value)
                {
                    return;
                }

                RaisePropertyChanging(GeneratedSampleNumberPropertyName);
                _generatedSampleNumber = value;
                RaisePropertyChanged(GeneratedSampleNumberPropertyName);
            }
        }

        /// <summary>
        /// The <see cref="InputSampleNumber" /> property's name.
        /// </summary>
        public const string InputSampleNumberPropertyName = "InputSampleNumber";

        private string _inputSampleNumber = string.Empty;

        /// <summary>
        /// Sets and gets the InputSampleNumber property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public string InputSampleNumber
        {
            get
            {
                return _inputSampleNumber;
            }

            set
            {
                if (_inputSampleNumber == value)
                {
                    return;
                }

                RaisePropertyChanging(InputSampleNumberPropertyName);
                _inputSampleNumber = value;
                RaisePropertyChanged(InputSampleNumberPropertyName);
            }
        }

        /// <summary>
        /// The <see cref="IsInputSampleNumberReadOnly" /> property's name.
        /// </summary>
        public const string IsInputSampleNumberReadOnlyPropertyName = "IsInputSampleNumberReadOnly";

        private bool _isInputSampleNumberReadOnly = false;

        /// <summary>
        /// Sets and gets the IsInputSampleNumberReadOnly property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public bool IsInputSampleNumberReadOnly
        {
            get
            {
                return _isInputSampleNumberReadOnly;
            }

            set
            {
                if (_isInputSampleNumberReadOnly == value)
                {
                    return;
                }

                RaisePropertyChanging(IsInputSampleNumberReadOnlyPropertyName);
                _isInputSampleNumberReadOnly = value;
                RaisePropertyChanged(IsInputSampleNumberReadOnlyPropertyName);
            }
        }

        /// <summary>
        /// The <see cref="IsFrontMilkSample" /> property's name.
        /// </summary>
        public const string IsFrontMilkSamplePropertyName = "IsFrontMilkSample";

        private bool _isFrontMilkSample = false;

        /// <summary>
        /// Sets and gets the IsFrontMilkSample property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public bool IsFrontMilkSample
        {
            get
            {
                return _isFrontMilkSample;
            }

            set
            {
                if (_isFrontMilkSample == value)
                {
                    return;
                }

                RaisePropertyChanging(IsFrontMilkSamplePropertyName);
                _isFrontMilkSample = value;
                RaisePropertyChanged(IsFrontMilkSamplePropertyName);
            }
        }

        /// <summary>
        /// The <see cref="IsMiddleMilkSample" /> property's name.
        /// </summary>
        public const string IsMiddleMilkSamplePropertyName = "IsMiddleMilkSample";

        private bool _isMiddleMilkSample = false;

        /// <summary>
        /// Sets and gets the IsMiddleMilkSample property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public bool IsMiddleMilkSample
        {
            get
            {
                return _isMiddleMilkSample;
            }

            set
            {
                if (_isMiddleMilkSample == value)
                {
                    return;
                }

                RaisePropertyChanging(IsMiddleMilkSamplePropertyName);
                _isMiddleMilkSample = value;
                RaisePropertyChanged(IsMiddleMilkSamplePropertyName);
            }
        }

        /// <summary>
        /// The <see cref="IsEndMilkSample" /> property's name.
        /// </summary>
        public const string IsEndMilkSamplePropertyName = "IsEndMilkSample";

        private bool _isEndMilkSample = false;

        /// <summary>
        /// Sets and gets the IsEndMilkSample property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public bool IsEndMilkSample
        {
            get
            {
                return _isEndMilkSample;
            }

            set
            {
                if (_isEndMilkSample == value)
                {
                    return;
                }

                RaisePropertyChanging(IsEndMilkSamplePropertyName);
                _isEndMilkSample = value;
                RaisePropertyChanged(IsEndMilkSamplePropertyName);
            }
        }

        /// <summary>
        /// The <see cref="IsFullMilkSample" /> property's name.
        /// </summary>
        public const string IsFullMilkSamplePropertyName = "IsFullMilkSample";

        private bool _isFullMilkSample = false;

        /// <summary>
        /// Sets and gets the IsFullMilkSample property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public bool IsFullMilkSample
        {
            get
            {
                return _isFullMilkSample;
            }

            set
            {
                if (_isFullMilkSample == value)
                {
                    return;
                }

                RaisePropertyChanging(IsFullMilkSamplePropertyName);
                _isFullMilkSample = value;
                RaisePropertyChanged(IsFullMilkSamplePropertyName);
            }
        }

        /// <summary>
        /// The <see cref="IsUnmatureMilkStage" /> property's name.
        /// </summary>
        public const string IsUnmatureMilkStagePropertyName = "IsUnmatureMilkStage";

        private bool _isUnmatureMilkStage = false;

        /// <summary>
        /// Sets and gets the IsUnmatureMilkStage property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public bool IsUnmatureMilkStage
        {
            get
            {
                return _isUnmatureMilkStage;
            }

            set
            {
                if (_isUnmatureMilkStage == value)
                {
                    return;
                }

                RaisePropertyChanging(IsUnmatureMilkStagePropertyName);
                _isUnmatureMilkStage = value;
                RaisePropertyChanged(IsUnmatureMilkStagePropertyName);
            }
        }

        /// <summary>
        /// The <see cref="IsTransitionalMilkStage" /> property's name.
        /// </summary>
        public const string IsTransitionalMilkStagePropertyName = "IsTransitionalMilkStage";

        private bool _isTransitionalMilkStage = false;

        /// <summary>
        /// Sets and gets the IsTransitionalMilkStage property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public bool IsTransitionalMilkStage
        {
            get
            {
                return _isTransitionalMilkStage;
            }

            set
            {
                if (_isTransitionalMilkStage == value)
                {
                    return;
                }

                RaisePropertyChanging(IsTransitionalMilkStagePropertyName);
                _isTransitionalMilkStage = value;
                RaisePropertyChanged(IsTransitionalMilkStagePropertyName);
            }
        }

        /// <summary>
        /// The <see cref="IsMatureMilkStage" /> property's name.
        /// </summary>
        public const string IsMatureMilkStagePropertyName = "IsMatureMilkStage";

        private bool _isMatureMilkStage = false;

        /// <summary>
        /// Sets and gets the IsMatureMilkStage property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public bool IsMatureMilkStage
        {
            get
            {
                return _isMatureMilkStage;
            }

            set
            {
                if (_isMatureMilkStage == value)
                {
                    return;
                }

                RaisePropertyChanging(IsMatureMilkStagePropertyName);
                _isMatureMilkStage = value;
                RaisePropertyChanged(IsMatureMilkStagePropertyName);
            }
        }

        /// <summary>
        /// The <see cref="SelectedAnalysisResult" /> property's name.
        /// </summary>
        public const string SelectedAnalysisResultPropertyName = "SelectedAnalysisResult";

        private AllTestItemsViewModel _selectedAnalysisResult = null;

        /// <summary>
        /// Sets and gets the SelectedAnalysisResult property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public AllTestItemsViewModel SelectedAnalysisResult
        {
            get
            {
                return _selectedAnalysisResult;
            }

            set
            {
                if (_selectedAnalysisResult == value)
                {
                    return;
                }

                RaisePropertyChanging(SelectedAnalysisResultPropertyName);
                _selectedAnalysisResult = value;
                RaisePropertyChanged(SelectedAnalysisResultPropertyName);
            }
        }

        /// <summary>
        /// The <see cref="AnalysisResultList" /> property's name.
        /// </summary>
        public const string AnalysisResultListPropertyName = "AnalysisResultList";

        private ObservableCollection<AllTestItemsViewModel> _analysisResultList = new ObservableCollection<AllTestItemsViewModel>();

        /// <summary>
        /// Sets and gets the AnalysisResultList property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public ObservableCollection<AllTestItemsViewModel> AnalysisResultList
        {
            get
            {
                return _analysisResultList;
            }

            set
            {
                if (_analysisResultList == value)
                {
                    return;
                }

                RaisePropertyChanging(AnalysisResultListPropertyName);
                _analysisResultList = value;
                RaisePropertyChanged(AnalysisResultListPropertyName);
            }
        }

        private RelayCommand _generateSampleNumberCommand;

        /// <summary>
        /// Gets the GenerateSampleNumberCommand.
        /// </summary>
        public RelayCommand GenerateSampleNumberCommand
        {
            get
            {
                return _generateSampleNumberCommand
                    ?? (_generateSampleNumberCommand = new RelayCommand(ExecuteGenerateSampleNumberCommand));
            }
        }

        private void ExecuteGenerateSampleNumberCommand()
        {
            try
            {
                // Generate the sample number. Remove the first two numbers.
                GeneratedSampleNumber = ServiceFactory.CreateUnassignedSampleNumberService().GetSampleNumber().Substring(2);

                // Print sample number as barcode via configured printer by Zebra using ZPL command
                var zplCommand = string.Format(ConfigFileUtility.GetValue(ConfigurationConstant.ZebraZPLCommand).ToString(),
                    GeneratedSampleNumber, GetCurrentMilkSamplePinYinDescription());

                var barCodePrinterName = ConfigFileUtility.GetValue(ConfigurationConstant.BarCodePrinterName).ToString();

                RawPrinterHelper.SendStringToPrinter(barCodePrinterName, zplCommand);
            }
            catch (System.Exception ex)
            {
                RequestDisplayErrorMessage(ex.Message);
            }
        }

        private RelayCommand _getAnalysisResultCommand;

        /// <summary>
        /// Gets the GetAnalysisResultCommand.
        /// </summary>
        public RelayCommand GetAnalysisResultCommand
        {
            get
            {
                return _getAnalysisResultCommand ?? (_getAnalysisResultCommand = new RelayCommand(
                    ExecuteGetAnalysisResultCommand,
                    CanExecuteGetAnalysisResultCommand));
            }
        }

        private void ExecuteGetAnalysisResultCommand()
        {
            try
            {
                AnalysisResultList = new ObservableCollection<AllTestItemsViewModel>(ViewModelHelper.RetrieveAnalysisResultFromMiniLog(InputSampleNumber, CurrentMilkStage));

                IsInputSampleNumberReadOnly = AnalysisResultList.Count > 0;

                (new RegistrationCodeConfigurationService()).DecreaseAvailableAuthorisedUsageCount();

                // If there is device realted message, then it needs to be cleared.
                RequestClearErrorMessage();
            }
            catch (Exception ex)
            {
                RequestDisplayErrorMessage(ex.Message);
            }
        }

        private bool CanExecuteGetAnalysisResultCommand()
        {
            return !string.IsNullOrEmpty(InputSampleNumber);
        }

        private RelayCommand _unassignedSampleNumberCommand;

        /// <summary>
        /// Gets the UnassignedSampleNumberCommand.
        /// </summary>
        public RelayCommand UnassignedSampleNumberCommand
        {
            get
            {
                return _unassignedSampleNumberCommand
                    ?? (_unassignedSampleNumberCommand = new RelayCommand(
                                          () =>
                                          {
                                              RequestGotoPage(new DeviceUnassignedSampleNumberListPage());
                                          }));
            }
        }

        private RelayCommand _nextCommand;

        /// <summary>
        /// Gets the NextCommand.
        /// </summary>
        public RelayCommand NextCommand
        {
            get
            {
                return _nextCommand ?? (_nextCommand = new RelayCommand(
                    ExecuteNextCommand,
                    CanExecuteNextCommand));
            }
        }

        private void ExecuteNextCommand()
        {
            try
            {
                var existSampleNumberCount = ServiceFactory.CreateAnalysisResultService().GetRowCount(p => p.SampleNumber == InputSampleNumber);

                if (existSampleNumberCount == 0)
                {
                    SetCurrentMilkSample();
                    SetCurrentMilkStage();
                    SetCurrentAnalysisResult();

                    // Send GoToNextPageMessage to MainPage.
                    Messenger.Default.Send<MainPageViewModel>(this, GoToNextPageMessageToken);
                }
                else
                {
                    RequestDisplayMessage(string.Format("样本号{0}已被分配", InputSampleNumber));
                }
            }
            catch (Exception ex)
            {
                RequestDisplayErrorMessage(ex.Message);
            }
        }

        private bool CanExecuteNextCommand()
        {
            if ((!IsFrontMilkSample && !IsMiddleMilkSample && !IsEndMilkSample && !IsFullMilkSample) ||
                (!IsUnmatureMilkStage && !IsTransitionalMilkStage && !IsMatureMilkStage) ||
                SelectedAnalysisResult == null)
            {
                return false;
            }

            return true;
        }

        /// <summary>
        /// Set CurrentMilkSample property based on user selection.
        /// </summary>
        private void SetCurrentMilkSample()
        {
            if (IsFrontMilkSample)
            {
                CurrentMilkSample = MilkSample.Front;
            }
            else if (IsMiddleMilkSample)
            {
                CurrentMilkSample = MilkSample.Middle;
            }
            else if (IsEndMilkSample)
            {
                CurrentMilkSample = MilkSample.End;
            }
            else if (IsFullMilkSample)
            {
                CurrentMilkSample = MilkSample.Full;
            }
        }

        /// <summary>
        /// Set CurrentMilkStage property based on user selection.
        /// </summary>
        private void SetCurrentMilkStage()
        {
            if (IsUnmatureMilkStage)
            {
                CurrentMilkStage = MilkStage.Unmature;
            }
            else if (IsTransitionalMilkStage)
            {
                CurrentMilkStage = MilkStage.Transitional;
            }
            else if (IsMatureMilkStage)
            {
                CurrentMilkStage = MilkStage.Mature;
            }
        }

        /// <summary>
        /// Get the Chinese PinYin description of CurrentMilkStage.
        /// </summary>
        /// <returns></returns>
        private string GetCurrentMilkStagePinYinDescription()
        {
            var milkStagePinYinDescription = string.Empty;

            if (IsUnmatureMilkStage)
            {
                milkStagePinYinDescription = "Chu Ru";
            }
            else if (IsTransitionalMilkStage)
            {
                milkStagePinYinDescription = "Guo Du Ru";
            }
            else if (IsMatureMilkStage)
            {
                milkStagePinYinDescription = "Cheng Shu Ru";
            }

            return milkStagePinYinDescription;
        }

        /// <summary>
        /// Get the Chinese PinYin description of CurrentMilkSample.
        /// </summary>
        /// <returns></returns>
        private string GetCurrentMilkSamplePinYinDescription()
        {
            var milkSamplePinYinDescription = string.Empty;

            if (IsFrontMilkSample)
            {
                milkSamplePinYinDescription = "Qian Duan";
            }
            else if (IsMiddleMilkSample)
            {
                milkSamplePinYinDescription = "Zhong Duan";
            }
            else if (IsEndMilkSample)
            {
                milkSamplePinYinDescription = "Hou Duan";
            }
            else if (IsFullMilkSample)
            {
                milkSamplePinYinDescription = "Quan Ru";
            }

            return milkSamplePinYinDescription;
        }

        /// <summary>
        /// Set CurrentAnalysisResult property based on selected analysis result.
        /// </summary>
        private void SetCurrentAnalysisResult()
        {
            CurrentAnalysisResult = new AnalysisResult
            {
                SampleNumber = InputSampleNumber,
                TestDate = SelectedAnalysisResult.TestDate,
                SampleType = (byte)CurrentMilkSample,
                SampleStageType = (byte)CurrentMilkStage,
                Fat = SelectedAnalysisResult.Fat.TestResult,
                CrudeProtein = SelectedAnalysisResult.CrudeProtein.TestResult,
                TrueProtein = SelectedAnalysisResult.TrueProtein.TestResult,
                Carbohydrate = SelectedAnalysisResult.Carbohydrate.TestResult,
                TS = SelectedAnalysisResult.TS.TestResult,
                Energy = SelectedAnalysisResult.Energy.TestResult
            };
        }

        #region Temporary use ONLY. Will be removed soon.
        private void GenerateDammyAnalysisResultData()
        {
            AnalysisResultList.Clear();

            AnalysisResultList.Add(new AllTestItemsViewModel(null, CurrentMilkStage)
            {
                SequenceNumber = 1,
                TestDate = GeneralUtility.GetCurrentDateTime(),
                TestDateDisplay = GeneralUtility.GetCurrentDateTime().ToString(GeneralUtility.LongDateTimeFormat),
                Fat = new TestItemFatViewModel(null, SharedEntityStorage.AnalysisItemList, CurrentMilkStage)
                {
                    TestResult = 2.46
                },
                CrudeProtein = new TestItemCrudeProteinViewModel(null, SharedEntityStorage.AnalysisItemList, CurrentMilkStage)
                {
                    TestResult = 1.88
                },
                TrueProtein = new TestItemTrueProteinViewModel(null, SharedEntityStorage.AnalysisItemList, CurrentMilkStage)
                {
                    TestResult = 1.64
                },
                Carbohydrate = new TestItemCarbohydrateViewModel(null, SharedEntityStorage.AnalysisItemList, CurrentMilkStage)
                {
                    TestResult = 6.3
                },
                TS = new TestItemTSViewModel(null, SharedEntityStorage.AnalysisItemList, CurrentMilkStage)
                {
                    TestResult = 12.1
                },
                Energy = new TestItemEnergyViewModel(null, SharedEntityStorage.AnalysisItemList, CurrentMilkStage)
                {
                    TestResult = 68.5
                }
            });

            AnalysisResultList.Add(new AllTestItemsViewModel(null, CurrentMilkStage)
            {
                SequenceNumber = 2,
                TestDate = GeneralUtility.GetCurrentDateTime().AddSeconds(-1000),
                TestDateDisplay = GeneralUtility.GetCurrentDateTime().AddSeconds(-1000).ToString(GeneralUtility.LongDateTimeFormat),
                Fat = new TestItemFatViewModel(null, SharedEntityStorage.AnalysisItemList, CurrentMilkStage)
                {
                    TestResult = 2.95
                },
                CrudeProtein = new TestItemCrudeProteinViewModel(null, SharedEntityStorage.AnalysisItemList, CurrentMilkStage)
                {
                    TestResult = 1.88
                },
                TrueProtein = new TestItemTrueProteinViewModel(null, SharedEntityStorage.AnalysisItemList, CurrentMilkStage)
                {
                    TestResult = 1.64
                },
                Carbohydrate = new TestItemCarbohydrateViewModel(null, SharedEntityStorage.AnalysisItemList, CurrentMilkStage)
                {
                    TestResult = 6.3
                },
                TS = new TestItemTSViewModel(null, SharedEntityStorage.AnalysisItemList, CurrentMilkStage)
                {
                    TestResult = 12.1
                },
                Energy = new TestItemEnergyViewModel(null, SharedEntityStorage.AnalysisItemList, CurrentMilkStage)
                {
                    TestResult = 68.5
                }
            });

            AnalysisResultList.Add(new AllTestItemsViewModel(null, CurrentMilkStage)
            {
                SequenceNumber = 3,
                TestDate = GeneralUtility.GetCurrentDateTime().AddSeconds(-2000),
                TestDateDisplay = GeneralUtility.GetCurrentDateTime().AddSeconds(-2000).ToString(GeneralUtility.LongDateTimeFormat),
                Fat = new TestItemFatViewModel(null, SharedEntityStorage.AnalysisItemList, CurrentMilkStage)
                {
                    TestResult = 2.95
                },
                CrudeProtein = new TestItemCrudeProteinViewModel(null, SharedEntityStorage.AnalysisItemList, CurrentMilkStage)
                {
                    TestResult = 1.88
                },
                TrueProtein = new TestItemTrueProteinViewModel(null, SharedEntityStorage.AnalysisItemList, CurrentMilkStage)
                {
                    TestResult = 1.64
                },
                Carbohydrate = new TestItemCarbohydrateViewModel(null, SharedEntityStorage.AnalysisItemList, CurrentMilkStage)
                {
                    TestResult = 6.3
                },
                TS = new TestItemTSViewModel(null, SharedEntityStorage.AnalysisItemList, CurrentMilkStage)
                {
                    TestResult = 12.1
                },
                Energy = new TestItemEnergyViewModel(null, SharedEntityStorage.AnalysisItemList, CurrentMilkStage)
                {
                    TestResult = 68.5
                }
            });
        }
        #endregion
    }
}